#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <cstdlib>
#include <unistd.h>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/sendfile.h>

#include "Utill.hpp"
#include "Log.hpp"

#define SERVER_ERROR 500
#define BAD_REQUEST 400        // 错误请求，返回400
#define HOME_PAGE "index.html" // 该文件夹下的首页文件
#define PAGE_404 "404.html"    // 错误页面的添加路径html
#define WEB_ROOT "wwwroot"     // 返回网络响应的路径
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define SEP ": "      // 报头键值对分隔符
#define OK 200        // 默认响应状态码
#define NOT_FOUND 404 // 非Cgi下，没有找到指定路径

// 状态码转换为描述
static std::string Code2Desc(int code)
{
    std::string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        break;
    }
    return desc;
}
// 响应的文件后缀转化为http描述
static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
    };

    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        return iter->second;
    }
    // 没有找到对应的形式，返回默认的text/html
    return "text/html";
}
// 收到的所有解析出来的请求信息类
class HttpRequest
{
public:
    std::string request_line;                // 头一行，包含请求方式，协议及其类型
    std::vector<std::string> request_header; // 报头，键值对
    std::string blank;                       // 空行，与报文的分隔
    std::string request_body;                // 报文内容

    // 解析完以后得结果
    std::string method; // POST/GET方法
    std::string url;    // path?args 访问路径和参数
    std::string version;

    std::unordered_map<std::string, std::string> header_kv; // 存储报头的键值对信息
    int content_length;
    std::string path;         // 访问路径
    std::string suffix;       // 访问文件的形式HTML/css
    std::string qurey_string; // 请求所带的参数

    bool cgi; // 判定是否需要采用cgi标识符
    int size; // 报文长度

public:
    HttpRequest() : content_length(0)
    {
    }

    ~HttpRequest()
    {
    }
};
// 构造的所有响应信息类
class HttpResponse
{
public:
    std::string status_line;
    std::vector<std::string> response_header;
    std::string blank;
    std::string response_body;

    int status_code; // 响应状态码
    int fd;          // 响应返回的资源文件标识符
public:
    HttpResponse() : blank(LINE_END), status_code(OK), fd(-1)
    {
    }

    ~HttpResponse()
    {
    }
};

// 读取请求，分析请求，构建响应
class EndPoint
{
private:
    int sock;
    HttpRequest http_request;
    HttpResponse http_response;
    bool stop;

private:
    // 接收报文报头,返回的是stop状态，成功stop为false：表示不停止loop
    bool RecvHttpRequestLine()
    {
        auto &line = http_request.request_line;
        if (Utill::ReadLine(sock, line) > 0)
        {
            line.resize(line.size() - 1); // 消除后面的\n
            LOG(INFO, http_request.request_line);
        }
        else
        {
            stop = true;
        }
        std::cout << "RecvHttpRequestLine" << stop << std::endl;
        return stop;
    }

    // 按行接收报文键值对
    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            if (Utill::ReadLine(sock, line) <= 0)
            {
                stop = true;
                break;
            }
            if (line == "\n")
            {
                http_request.blank = line;
                break;
            }

            line.resize(line.size() - 1); // 消除末端\n
            http_request.request_header.push_back(line);
            LOG(INFO, line);
        }

        std::cout << "stop debug" << stop << std::endl;
        return stop;
    }

    // 解析报头数据
    void ParseHttpRequestLine()
    {
        auto &line = http_request.request_line;
        std::stringstream ss(line); // 根据分割符将字符串打散输出
        ss >> http_request.method >> http_request.url >> http_request.version;
        auto &method = http_request.method;
        // 从method，begin（）开始，到end（），以method。begin（）开始，做toupper操作
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }

    // 解析报文键值对
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        // 分割报头的键值对
        for (auto &iter : http_request.request_header)
        {
            if (Utill::CutString(iter, key, value, SEP))
            {
                http_request.header_kv.insert(key, value);
            }
        }
    }

    // 判断请求方式，并且确定报文正文长度
    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = http_request.method;
        if (method == "POST")
        {
            auto &header_kv = http_request.header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                LOG(INFO, "Post Mehtod,Content-Length:" + iter->second);
                http_request.content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }

    // 接收报文正文
    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = http_request.content_length;
            auto &body = http_request.request_body;

            // 一个字符一个字符的读取报文内容
            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                {
                    stop = true;
                    break;
                }
            }
            LOG(INFO, body);
        }
        return stop;
    }

    int ProcessCgi()
    {
        // Cgi模式下处理报文
        LOG(INFO, "process cgi method");

        int code = OK; // 状态码,以此作为该函数的下一步执行判断

        // 父进程数据，作为
        auto &method = http_request.method;
        auto &query_string = http_request.qurey_string; // GET方式下获得的参数
        auto &body_text = http_request.request_body;    // POST方式下获得正文内容
        auto &bin = http_request.path;                  // 让子进程执行目标程序的地址
        int content_length = http_request.content_length;
        auto &response_body = http_response.response_body;

        std::string query_string_env; // 子进程传入的环境变量
        std::string method_env;
        std::string content_length_env;

        // 父进程创建管道，穿个子进程参数和环境变量，得到子进程结果
        int input[2];
        int output[2];
        // 创建两个单向管道文件，pipe[0]为读端，pipe[1]为写端
        if (pipe(input) < 0)
        {
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        if (pipe(output) < 0)
        {
            LOG(ERROR, "pipe output error");
            code = SERVER_ERROR;
            return code;
        }

        // 创建新的子进程，子进程执行对应的CGI程序
        pid_t pid = fork();
        // 子进程执行流
        if (pid == 0)
        {
            close(input[0]);  // 即input[1]作为写端，向父进程输出
            close(output[1]); // 即output[0]作为读端，由父进程写进

            method_env = "METHOD=";
            method_env += method;
            // 父进程无法给导入对应的环境变量，
            // 因为在替换程序中，上下文的文件描述符，输入参数替换为其他程序代码
            putenv((char *)method_env.c_str());

            if (method == "GET")
            {
                // get方法下参数
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;
                putenv((char *)query_string_env.c_str());
                LOG(INFO, "Get Method,Add Query_string Env");
            }
            else if (method == "POST")
            {
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char *)content_length_env.c_str());
                LOG(INFO, "Post Method,Add Content_Length Env");
            }
            else
            {
                // do nothing
            }

            // 要替换程序时怎样让子进程得知对应的读写文件的描述符呢？
            // 直接将stdin，stdout重定向到对应的output[0],input[0],
            dup2(output[0], 0);
            dup2(input[1], 1);

            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid < 0)
        {
            LOG(ERROR, "fork error");
            return 404;
        }
        else
        {
            // 父进程的文件描述符的关闭，对应子进程：
            //  close(input[0]);//即input[1]作为写端，向父进程输出
            //  close(output[1]);//即output[0]作为读端，由父进程写进
            close(input[1]);  // 父进程由input[0]中读取
            close(output[0]); // 父进程由output[1]中写入

            if (method == "POST")
            {
                const char *start = body_text.c_str();
                int total = 0;
                int size = 0;
                // 向对应的文件描述符output[1]中循环写入，判断写入的字数
                // write：文件描述符：写入字符串缓冲区地址，字符串长度
                while (total < content_length && (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
                    total += size;
                }
            }

            char ch = 0; // 接受子进程输出内容
            // read:文件描述符：写入的地址缓冲区：写入数量（字节）
            while (read(input[0], &ch, 1) > 0)
            {
                response_body.push_back(ch);
            }

            // 接收子进程退出的状态码
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                // 检查对应的退出状态
                if (WIFEXITED(status))
                {
                    if (WEXITSTATUS(status) == 0)
                    {
                        code = OK;
                    }
                    else
                    {
                        code = BAD_REQUEST;
                    }
                }
                else
                {
                    code = SERVER_ERROR;
                }
            }
            close(input[0]);
            close(output[1]);
        }
        return code;
    }

    // 报文不符合，设置对应错误的http_response响应，返回404页面
    void HandlerError(std::string page)
    {
        std::cout << "debug" << page << std::endl;
        http_request.cgi = false;
        // 给用户返回对应的404页面
        http_response.fd = open(page.c_str(), O_RDONLY);
        if (http_response.fd > 0)
        {
            struct stat st;
            // 检查对应路径下的文件的各种信息，存储在st结构体中
            stat(page.c_str(), &st);
            http_request.size = st.st_size;

            // 设置响应报头信息,键值对
            std::string line = "Content_Type:text/html";
            line += LINE_END;
            http_response.response_header.push_back(line);
            line = "Content-Length:";
            line += std::to_string(st.st_size);
            line += LINE_END;
            http_response.response_header.push_back(line);
        }
    }

    // 处理非Cgi模式下，直接返回路径网页
    int ProcessNonCgi()
    {
        http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_response.fd >= 0)
        {
            LOG(INFO, http_request.path + "open success!");
            return OK;
        }
        return NOT_FOUND;
    }

    void BuildOKResponse()
    {
        std::string line = "Content-Type:";
        line += Suffix2Desc(http_request.suffix);
        line += LINE_END;
        http_response.response_header.push_back(line);

        line = "Content-Length: ";
        if (http_request.cgi)
        {
            line += std::to_string(http_response.response_body.size());
        }
        else
        {
            line += std::to_string(http_request.size); // GET方式
        }

        line += LINE_END;
        http_response.response_header.push_back(line);
    }

    void BuildHttpResponseHelper()
    {
        auto &code = http_response.status_code;
        // 构建报文状态行 如：HTPP/1.0 200  OK\r\n
        auto &status_line = http_response.status_line;
        status_line += HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += Code2Desc(code);
        status_line += LINE_END;
        std::string path = WEB_ROOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOKResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        case BAD_REQUEST:
            path += PAGE_404;
            HandlerError(path);
            break;
        case SERVER_ERROR:
            path += PAGE_404;
            HandlerError(path);
            break;
        default:
            break;
        }
    }

public:
    EndPoint(int _sock) : sock(_sock), stop(false)
    {
    }
    bool IsStop()
    {
        return stop;
    }

    void RecvHttpRequest()
    {
        // 短路求值：判断首行和报头是否拿到
        if ((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()))
        {
            // 解析首行和报头
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            // 接受报文正文
            RecvHttpRequestBody();
        }
    }

    void BuildHttpResponse()
    {
        // 这几个变量均为goto语句下的提前声明
        std::string _path;
        struct stat st;
        // http_request.path中查找到的文件形式的后缀的位置
        std::size_t found = 0;
        auto &code = http_response.status_code;

        // 不为GET或者POST请求视为非法请求
        if (http_request.method != "GET" && http_request.method != "POST")
        {
            std::cout << "method" << http_request.method << std::endl;
            LOG(WARING, "method is not right");
            code = BAD_REQUEST;
            goto END;
        }
        if (http_request.method == "GET")
        {
            // 拆分请求的参数
            size_t pos = http_request.url.find('?');
            if (pos != std::string::npos)
            {
                Utill::CutString(http_request.url, http_request.path, http_request.qurey_string, "?");
                http_request.cgi = true;
            }
            else
            {
                http_request.path = http_request.url;
            }
        }
        else if (http_request.method == "POST")
        {
            // POST
            http_request.cgi = true;
            http_request.path = http_request.url;
        }
        else
        {
            // do nothing
        }

        // 设置文件路径由当前的WEB_ROOT开始
        _path = http_request.path;
        http_request.path = WEB_ROOT;
        http_request.path += _path;
        // 判断请求的是文件夹还是具体文件，文件夹则返回该文件夹下的首页
        if (http_request.path[http_request.path.size() - 1] == '/')
        {
            http_request.path += HOME_PAGE;
        }

        // 检查对应路径下是否有对应文件
        if (stat(http_request.path.c_str(), &st) == 0)
        {
            // 判断该文件为文件夹，加上"/HOME_PAGE"，返回对应的首页
            if (S_ISDIR(st.st_mode))
            {
                http_request.path += '/';
                http_request.path += HOME_PAGE;
                stat(http_request.path.c_str(), &st);
            }
            // 判断该文件的是否是具有可执行权限的文件
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 特殊处理
                http_request.cgi = true;
            }
            // 请求的文件大小
            http_request.size = st.st_size;
        }
        else
        {
            // 说明该资源不存在
            LOG(WARING, http_request.path + "not found");
            code = NOT_FOUND;
            goto END;
        }

        found = http_request.path.rfind(".");
        if (found == std::string::npos)
        {
            // 没有找到对应文件类型，默认为是html
            http_request.suffix = ".html";
        }
        else
        {
            // 存在则截取出对应的后缀
            http_request.suffix = http_request.path.substr(found);
        }
        if (http_request.cgi == true)
        {
            code = ProcessCgi(); // 执行目标程序
        }
        else
        {
            // 1.前面已经检查了对应文件的存在
            // 根据已有的转态行然后构建对应的响应
            code = ProcessNonCgi();
        }
    END:

        // 在已有的http_response的内容下构建对应http报头，状态行，键值对和空行
        BuildHttpResponseHelper();
    }
    void SendHttpResponse()
    {
        // 逐行将http_response中的信息发送
        send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
        for (auto iter : http_response.response_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);
        }
        send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
        if (http_request.cgi)
        {
            auto &response_body = http_response.response_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = response_body.c_str();
            while (total < response_body.size() && (size = send(sock, start + total, response_body.size() - total, 0)) > 0)
            {
                total += size;
            }
        }
        else
        {
            // 非cgi模式下直接使用sendfile将对应文件传输到对应套接字文件中
            sendfile(sock, http_response.fd, nullptr, http_request.size);
            close(http_response.fd);
        }
    }
    ~EndPoint()
    {
        close(sock);
    }
};

//构建回调函数封装底层实现函数
class CallBlack
{
public:
    CallBlack()
    {
    }
    void operator()(int sock)
    {
        HandlerReuqest(sock);
    }

    void HandlerReuqest(int sock)
    {
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        // 非停止
        if (!ep->IsStop())
        {
            LOG(INFO, "Recv No Error,Begin Build And Send");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(WARING, "Recv Error,Stop Build And Send");
        }
        delete ep;
        LOG(INFO,"Hander Request End");
    }
    ~CallBlack();
};
